package sxe.core.cs511.team2_2;

import snobject.*;
import step.EvaluationFailure;
import step.FunctionWrapper;
import sxe.core.hash.SQLClientInstance;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.*;

/** OVERVIEW:  A class that defines the opcode gettable.  This opcode will take as arguments a start and end
 *  time and retrieve all values (entered at a fixed 1 minute granularity) from a SQL database for the time interval
 *  specified.
 *  The abstraction function is AF(c) = {two Date Objects specifying start and end time and a structure containing the data for this time interval}.
 *  The representation invariant is that after initialization starttime must be < endtime and neither object can be null.
 *  The data Vector can not contain any null entries.
 * @author lisap
 *
 */

public class gettable extends FunctionWrapper {

	private Date starttime;
	private Date endtime;
	private snStruct data;
	private int count = 0;
	
	/**EFFECTS:  This method returns a Vector of Objects for the time period specified in the argument list. 
	 * This method is called when this opcode is called in a STEP program.
	 * @param argv The arglist that was specified in the STEP program containing a string representation of the start and end time.
	 * @return a Vector of Objects that represents the data for the given time interval. 
	 * @throws EvaluationFailure if startime or endtime are not formatted correctly or if starttime < endtime

	 */
	public snObject Call(snObjectArgList argv) throws EvaluationFailure{
		count = 0;
		data = new snStruct();
		snString startstring = argv.popString();
		snString endstring = argv.popString();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
		try{
		starttime = sdf.parse(startstring.getString());
		} catch (ParseException e){
			System.err.println("start time is badly formatted : " + startstring);
			throw new EvaluationFailure(this.expnode, "start time is badly formatted : " + startstring);
		}
		try{
		endtime = sdf.parse(endstring.getString());
		} catch (ParseException e){
			System.err.println("endtime is badly formatted : " + endtime);
			throw new EvaluationFailure(this.expnode, "endtime is badly formatted : " + endtime);
		}
		// loop through possible store times and do a retrieve for each time interval
		long query = (long)(starttime.getTime() /1000) * 1000;
		if (starttime.getTime() > query)
			query = query + 1000;
		while (query <= endtime.getTime()){
			String key = getKey(query);
			snStruct ministruct;
			try{
				ministruct = (snStruct)getData(key);
			} catch(ClassCastException e){
				System.err.println("gettable expects value associated with key " + key + " to be of type snStruct.");
				throw new EvaluationFailure(this.expnode, "gettable expects value associated with key " + key + " to be of type snStruct.");
			} catch (SQLException sql){
				System.err.println("No entry for key " + key);
				throw new EvaluationFailure(this.expnode, "No entry for key " + key);
			}
		
			if (ministruct != null){
				// add contents of ministruct to data struct
				Collection<snObject> col = ministruct.getValues();
				Iterator it = col.iterator();
				while (it.hasNext()){
					snObject obj = (snObject)it.next();
					data.put(new snString("" + count), obj);
					count++;
				}
			}
			query = query + 1000;
		}
		return data;
	}
	
	/** EFFECTS:  This helper method generates a String key that corresponds to the specified time.
	 *  This key can be used to look up an entry in the SQL database.
	 * @param time The time for which to create the key.
	 * @return The String key associated with the specified time.
	 */
	private String getKey(long time){
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
		Date adate = new Date(time);
		return sdf.format(adate);
	}
	

	/** EFFECTS:  This helper method does one SQL query for the data associated with the specified key.
	 * @param key The key for which to look for SQL data
	 * @return The Object returned by the SQL query for the speficied key, null if the query returned no data for this key.
	 */
	private snObject getData(String key) throws SQLException{
		return SQLClientInstance.getInstance().Get(key);
	}
	

}
